﻿Imports System.Text
Imports System.Xml
Imports System.Net
Imports System.Web

Partial Class TwitterClient
    ''' <summary>
    ''' Twitterへのログイン結果を表現します。
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum LoginResult
        Success
        Failed
        ErrorOccured
    End Enum

    ''' <summary>
    ''' Twitterへログインする処理中のデータを保持します。
    ''' </summary>
    ''' <remarks></remarks>
    Private Class LoginBundle
        Private _result As LoginResult
        Public Property Result() As LoginResult
            Get
                Return _result
            End Get
            Set(ByVal value As LoginResult)
                _result = value
            End Set
        End Property

        Private _authenticityToken As String
        Public Property AuthenticityToken() As String
            Get
                Return _authenticityToken
            End Get
            Set(ByVal value As String)
                _authenticityToken = value
            End Set
        End Property

        Private _authenticityTokenFiledName As String
        Public Property AuthenticationTokenFiledName() As String
            Get
                Return _authenticityTokenFiledName
            End Get
            Set(ByVal value As String)
                _authenticityTokenFiledName = value
            End Set
        End Property

        Private _userNameFieldName As String
        Public Property UserNameFieldName() As String
            Get
                Return _userNameFieldName
            End Get
            Set(ByVal value As String)
                _userNameFieldName = value
            End Set
        End Property

        Private _passwordFieldName As String
        Public Property PasswordFieldName() As String
            Get
                Return _passwordFieldName
            End Get
            Set(ByVal value As String)
                _passwordFieldName = value
            End Set
        End Property
    End Class

    ''' <summary>
    ''' Twitterにログインします。
    ''' </summary>
    ''' <param name="userName">ユーザー名</param>
    ''' <param name="password">パスワード</param>
    ''' <returns>ログイン結果</returns>
    ''' <remarks></remarks>
    Public Function Login(ByVal userName As String, ByVal password As String) As LoginResult
        Dim bundle As LoginBundle = LoginGet()
        Select Case bundle.Result
            Case LoginResult.Success
                LoginPost(userName, password, bundle)
        End Select
        Return bundle.Result
    End Function

    ''' <summary>
    ''' Twitterのログイン画面を取得して解析します。
    ''' </summary>
    ''' <returns>処理中のデータを保持するオブジェクト</returns>
    ''' <remarks></remarks>
    Private Function LoginGet() As LoginBundle
        Dim bundle As New LoginBundle
        Dim url As String = My.Resources.TwitterUrl
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterGetHeaders(Nothing, _cookies)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.OK
                    res.LoadCookies(_cookies)
                    Dim xmlRes As XmlDocument = res.GetResponseAsXHtml()
                    Dim xnm As XmlNamespaceManager = xmlRes.GetXHtmlNameSpaceManager("x")
                    bundle.AuthenticityToken = xmlRes.SelectSingleNode("//x:input[@id='authenticity_token']", xnm).Attributes("value").Value
                    bundle.AuthenticationTokenFiledName = xmlRes.SelectSingleNode("//x:input[@id='authenticity_token']", xnm).Attributes("name").Value
                    bundle.UserNameFieldName = xmlRes.SelectSingleNode("//x:input[@id='username']", xnm).Attributes("name").Value
                    bundle.PasswordFieldName = xmlRes.SelectSingleNode("//x:input[@id='password']", xnm).Attributes("name").Value
                    bundle.Result = LoginResult.Success
                Case Else
                    bundle.Result = LoginResult.ErrorOccured
            End Select
        End Using
        Return bundle
    End Function

    ''' <summary>
    ''' Twitterにログイン情報をポストして結果を取得します。
    ''' </summary>
    ''' <param name="userName">ユーザー名</param>
    ''' <param name="password">パスワード</param>
    ''' <param name="bundle">処理中のデータを保持するLoginBundle</param>
    ''' <remarks></remarks>
    Private Sub LoginPost(ByVal userName As String, ByVal password As String, ByVal bundle As LoginBundle)
        Dim url As String = String.Format("{0}{1}", My.Resources.TwitterSecureUrl, My.Resources.TwitterSessionPath)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterPostHeaders("http://twitter.com/", _cookies)
        Dim sbPost As New StringBuilder()
        sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(bundle.AuthenticationTokenFiledName), HttpUtility.UrlEncode(bundle.AuthenticityToken))
        sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(bundle.UserNameFieldName), HttpUtility.UrlEncode(userName))
        sbPost.AppendFormat("{0}={1}", HttpUtility.UrlEncode(bundle.PasswordFieldName), HttpUtility.UrlEncode(password))
        Dim strPost As String = sbPost.ToString()
        req.PostString(strPost)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.Found
                    res.LoadCookies(_cookies)
                    _authenticityTokenFiledName = bundle.AuthenticationTokenFiledName
                    _authenticityToken = bundle.AuthenticityToken
                    _isLogined = True
                    _userName = userName
                    bundle.Result = LoginResult.Success
                Case HttpStatusCode.OK
                    bundle.Result = LoginResult.Failed
                Case Else
                    bundle.Result = LoginResult.ErrorOccured
            End Select
        End Using
    End Sub

    ''' <summary>
    ''' Twitterからのログアウト結果を表現します。
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum LogoutResult
        Success
        Failed
        ErrorOccured
    End Enum

    ''' <summary>
    ''' Twitterからログアウトします。
    ''' </summary>
    ''' <returns>ログアウト結果</returns>
    ''' <remarks></remarks>
    Public Function Logout() As LogoutResult
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim funcResult As LogoutResult
        Dim url As String = String.Format("{0}{1}", My.Resources.TwitterUrl, My.Resources.TwitterSessionDestroyPath)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterPostHeaders("http://twitter.com/", _cookies)
        Dim sbReq As New StringBuilder()
        sbReq.AppendFormat("{0}={1}", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
        Dim strReq As String = sbReq.ToString()
        req.PostString(strReq)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.Found
                    _cookies = New CookieContainer()
                    _authenticityToken = Nothing
                    _authenticityTokenFiledName = Nothing
                    _isLogined = False
                    funcResult = LogoutResult.Success
                Case HttpStatusCode.OK
                    funcResult = LogoutResult.Failed
                Case Else
                    funcResult = LogoutResult.ErrorOccured
            End Select
        End Using
        Return funcResult
    End Function
End Class
